package com.example.shop.user.controller;

import com.example.shop.common.api.ResponseResult;
import com.example.shop.common.api.ServiceStateCode;
import com.example.shop.common.bo.mail.template.CodeTemplate;
import com.example.shop.common.bo.mail.template.MailTemplateGenerator;
import com.example.shop.common.bo.sms.template.CodeTemplateParam;
import com.example.shop.common.dto.mail.TemplateMail;
import com.example.shop.common.dto.sms.ShortMessage;
import com.example.shop.pojo.User;
import com.example.shop.redis.service.LettuceClient;
import com.example.shop.user.vo.UserPublicInfo;
import com.example.shop.user.dto.RegisterParam;
import com.example.shop.common.api.ServiceReturn;
import com.example.shop.redis.enums.RedisKey;
import com.example.shop.common.enums.SessionAttributeKey;
import com.example.shop.user.dto.UserPersonalInfoParam;
import com.example.shop.user.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.session.Session;
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Base64;

@RestController("userController")
@RequestMapping("/user")
public class UserController {

    @Autowired
    UserService userService;
    @Autowired
    LettuceClient redisClient;
    @Autowired
    RedisIndexedSessionRepository sessionRepository;

    /**
     * 对注册信息进行校验
     * @param registerParam 注册的信息
     * @param bindingResult 存放注册信息的校验结果
     * @return 返回注册信息的校验结果
     */
    @PostMapping(value = "/register/param/check",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseResult<Boolean> checkRegisterParam(@Validated RegisterParam registerParam, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return ResponseResult.failure(ServiceStateCode.PARAMETER_NONSTANDARD);
        }
        ServiceReturn<Boolean> result = userService.checkRegisterParam(registerParam);
        return  ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }

    /**
     * 进行用户注册
     * @param registerParam 注册的信息
     * @param bindingResult 存放注册信息的校验结果
     * @return 返回注册结果
     */
    @PostMapping(value = "/register",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseResult<Boolean> register(@Validated RegisterParam registerParam, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return ResponseResult.failure(ServiceStateCode.PARAMETER_NONSTANDARD);
        }
        ServiceReturn<Boolean> checkResult = userService.checkRegisterParam(registerParam);
        if (checkResult.getReturnData()) {
            try {
                ServiceReturn<Boolean> result = userService.register(registerParam);
                return ResponseResult.auto(result.getStateCode(),result.getReturnData());
            }catch (Exception e){
                e.printStackTrace();
                return ResponseResult.failure(ServiceStateCode.FAILURE);
            }
        }
        return ResponseResult.booleanResp(checkResult.getStateCode(),checkResult.getReturnData());
    }

    /**
     * 开启人脸识别
     * @param base64 人脸图片base64字符串
     * @return 返回开启状态，开启成功返回true
     */
    @PostMapping(value = "/enable/faceRecognitionLogin",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> enableFaceRecognition(HttpSession session,@RequestBody String base64){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        ServiceReturn<Boolean> result = userService.enableFaceRecognition(user, base64);
        return ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }

    /**
     * 禁用人脸识别
     * @return 返回禁用状态，禁用成功返回true
     */
    @GetMapping(value = "/ban/faceRecognitionLogin",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> banFaceRecognition(HttpSession session){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        ServiceReturn<Boolean> result = userService.banFaceRecognition(user);
        return ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }

    /**
     * 登录后的处理
     * @param result 登录业务返回的结果
     * @return 返回处理结果
     */
    public ResponseResult<UserPublicInfo> loginAfterDeal(HttpSession session,ServiceReturn<User> result){
        //业务正确处理
        if(result.getStateCode().correctExecBusiness()){
            User user = result.getReturnData();
            //将其他session会话中的该用户进行下线。一个人只能在一个电脑上session登录
            String sessionId = (String) redisClient.hGet(RedisKey.HashMap.USER_LOGIN_IN_SESSION_ID.getKey(),String.valueOf(user.getUserId()));
            //不为null，有2种可能：1在本电脑上已经登录过了(不应该取删除处理)、2在其他电脑上登录(应该删除)
            if(sessionId!=null && sessionId!=session.getId()){
                //其他电脑上登录的session会话
                Session otherSession = sessionRepository.findById(sessionId);
                //删除掉session中保存的该用户，即该会话将不能获取该用户的信息
                session.removeAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
            }

            //将当前会话中登录的用户设置为该用户，即下次可以不用登录，直接自动登录
            //如果当前会话已经有其他用户登录了，则应该强行让该用户登出
            User noLoginUser = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
            if(noLoginUser != null && !noLoginUser.getUserId().equals(user.getUserId())){
                userService.loginOut(noLoginUser);
            }
            session.setAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey(),user);
            //记录该session登录着该用户
            redisClient.hPut(RedisKey.HashMap.USER_LOGIN_IN_SESSION_ID.getKey(), String.valueOf(user.getUserId()),session.getId());
            UserPublicInfo userPublicInfo = UserPublicInfo.getUserPublicInfo(user);
            return ResponseResult.success(userPublicInfo);
        }
        //有登录有错误，如密码不正确
        return ResponseResult.failure(result.getStateCode());
    }

    /**
     * 正常的账户密码登录
     * @param account 账户
     * @param password 密码
     * @return 返回登录结果
     */
    @PostMapping(value = "/login",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseResult<UserPublicInfo> login(HttpSession session, String account, String password){
        if(account==null || password == null){
            return ResponseResult.failure(ServiceStateCode.PARAMETER_NONSTANDARD);
        }
        //进行登录验证
        ServiceReturn<User> result = userService.login(account, password);
        return loginAfterDeal(session,result);
    }

    /**
     * 进行人脸识别登录
     * @param faceBase64  个人照的Base64字符串
     * @return 返回登录结果
     */
    @PostMapping(value = "/login/face/recognition_base64",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<UserPublicInfo> faceRecognitionLogin(HttpSession session, @RequestBody String faceBase64){
        ServiceReturn<User> result = userService.faceRecognitionLogin(faceBase64);
        return loginAfterDeal(session,result);
    }

    /**
     * 进行人脸识别登录
     * @param imageFile 人个图片，个人照
     * @return 返回登录结果
     */
    @PostMapping(value = "/login/face/recognition_file",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseResult<UserPublicInfo> faceRecognitionLogin(HttpSession session, MultipartFile imageFile){
        if (imageFile==null) {
            return ResponseResult.failure(ServiceStateCode.PARAMETER_NONSTANDARD);
        }
        boolean legalPicture = imageFile.getOriginalFilename().matches("/*\\.[jpg|JPG|PNG|png]/");
        if(legalPicture){
            return ResponseResult.failure(ServiceStateCode.IMAGE_ERROR_UNSUPPORTED_FORMAT);
        }
        try {
            byte[] bytes = imageFile.getBytes();
            String faceBase64 = Base64.getEncoder().encodeToString(bytes);
            return faceRecognitionLogin(session,faceBase64);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ResponseResult.failure(ServiceStateCode.FAILURE);
    }

    /**
     * 进行手机号登录
     * @param phoneNumber 登录的手机号
     * @param authCode 用户填写的验证码
     * @return 返回登录结果
     */
    @PostMapping(value = "/login/phone/check",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseResult<UserPublicInfo> smsLogin(HttpSession session, String phoneNumber, String authCode){
        //从redis中拿登录授权码，k-v：手机号:authCode 登录授权码
        ServiceReturn<User> result = userService.smsLogin(phoneNumber,authCode);
        return loginAfterDeal(session,result);
    }

    /**
     * 进行邮箱登录
     * @param email 登录的邮箱
     * @param authCode 用户填写的验证码
     * @return 返回登录结果
     */
    @PostMapping(value = "/login/email/check",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseResult<UserPublicInfo> emailLogin(HttpSession session, String email, String authCode){
        //从redis中拿登录授权码，k-v：邮箱:authCode 登录授权码
        ServiceReturn<User> result = userService.emailLogin(email, authCode);
        return loginAfterDeal(session,result);
    }

    /**
     * 发送一封登录短信到指定手机号上
     * @param phoneNumber 要发送到的手机号
     * @return 返回发送状态，发送成功为true，否则为false
     */
    @GetMapping(value = "/login/phone/send/{phoneNumber}",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> sendLoginSMS(HttpServletRequest req,@PathVariable("phoneNumber")String phoneNumber){
        String ip = req.getRemoteHost();
        //保证同一个ip不能多次发送
        synchronized (ip){
            //如果从redis中拿得到验证码，说明已经发送过短信了
            if(redisClient.get(RedisKey.USER_LOGIN_CODE +phoneNumber)!=null){
                return ResponseResult.failure(ServiceStateCode.SMS_HAS_BEEN_SENT);
            }
            return sendSMS("sendLoginAuthCodeToPhone",phoneNumber,"SMS_154950909");
        }
    }

    /**
     * 发送一封登录邮件到指定邮箱
     * @param to 要发送的邮箱
     * @return 返回发送状态，发送成功为true，否则为false
     */
    @GetMapping(value = "/login/email/send/{to}",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> sendLoginEmail(HttpServletRequest req,@PathVariable("to") String to){
        String ip = req.getRemoteHost();
        //保证同一个ip不能多次发送
        synchronized (ip){
            //如果从redis中拿得到验证码，说明已经发送过邮件了
            if(redisClient.get(RedisKey.USER_LOGIN_CODE +to)!=null){
                return ResponseResult.booleanResp(ServiceStateCode.EMAIL_HAS_BEEN_SENT,false);
            }
            return sendEmail("sendLoginAuthCodeToEmail","登录授权码","mail_login_auth_code.html",to);
        }
    }


    /**
     * 发送绑定邮箱的邮件
     * @param bindEmail 要绑定的邮箱
     * @return 返回发送状态
     */
    @GetMapping(value = "/bind/email/send/{bindEmail}",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> sendBindEmail(HttpSession session,HttpServletRequest req,@PathVariable("bindEmail") String bindEmail){
        String ip = req.getRemoteHost();
        //保证同一个ip不能多次发送
        synchronized (ip){
            //如果从redis中拿得到验证码，说明已经发送过邮件了
            if(redisClient.get(RedisKey.USER_BIND_CODE +bindEmail)!=null){
                return ResponseResult.booleanResp(ServiceStateCode.EMAIL_HAS_BEEN_SENT,false);
            }

            return sendEmail("sendBindAuthCodeToEmail","绑定邮箱","mail_bind_auth_code.html",bindEmail);
        }
    }

    /**
     * 发送绑定短信
     * @param bindPhone 要绑定的手机号
     * @return 返回发送状态
     */
    @GetMapping(value = "/bind/phone/send/{bindPhone}",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> sendBindPhoneSMS(HttpSession session,HttpServletRequest req,@PathVariable("bindPhone") String bindPhone){
        String ip = req.getRemoteHost();
        //保证同一个ip不能多次发送
        synchronized (ip){
            //如果从redis中拿得到验证码，说明已经发送过邮件了
            if(redisClient.get(RedisKey.USER_BIND_CODE +bindPhone)!=null){
                return ResponseResult.booleanResp(ServiceStateCode.SMS_HAS_BEEN_SENT,false);
            }
            return sendSMS("sendBindAuthCodeToPhone",bindPhone,"SMS_154950909");
        }
    }

    /**
     * 发送解除绑定邮箱的邮件到解绑的邮箱里
     * @param unbindEmail 解绑的邮箱
     * @return 返回发送状态，发送成功为true
     */
    @GetMapping(value = "/unbind/email/send/{unbindEmail}",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> sendUnbindEmail(HttpSession session,HttpServletRequest req,@PathVariable("unbindEmail") String unbindEmail){
        String ip = req.getRemoteHost();
        //保证同一个ip不能多次发送
        synchronized (ip){
            //如果从redis中拿得到验证码，说明已经发送过邮件了
            if(redisClient.get(RedisKey.USER_UNBIND_CODE +unbindEmail)!=null){
                return ResponseResult.booleanResp(ServiceStateCode.EMAIL_HAS_BEEN_SENT,false);
            }
            return sendEmail("sendUnbindAuthCodeToEmail","解绑邮箱","mail_unbind_auth_code.html",unbindEmail);
        }
    }

    /**
     * 发送解绑短信到解绑手机号里
     * @param bindPhone 解绑手机号
     * @return 返回发送状态，发送成功为true
     */
    @GetMapping(value = "/unbind/phone/send/{unbindPhone}",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> sendUnbindPhoneSMS(HttpSession session,HttpServletRequest req,@PathVariable("unbindPhone") String bindPhone){
        String ip = req.getRemoteHost();
        //保证同一个ip不能多次发送
        synchronized (ip){
            //如果从redis中拿得到验证码，说明已经发送过邮件了
            if(redisClient.get(RedisKey.USER_UNBIND_CODE +bindPhone)!=null){
                return ResponseResult.booleanResp(ServiceStateCode.SMS_HAS_BEEN_SENT,false);
            }
            return sendSMS("sendUnbindAuthCodeToPhone",bindPhone,"SMS_154950909");
        }
    }

    /**
     * 发送修改密码的邮件到指定的邮箱(该邮箱对应要修改密码的用户)里
     * @param email 邮箱(该邮箱对应要修改密码的用户)
     * @return 返回发送状态，发送成功为true
     */
    @GetMapping(value = "/change/password/email/send/{email}",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> sendChangePasswordToEmail(HttpSession session,HttpServletRequest req,@PathVariable("email") String email){
        String ip = req.getRemoteHost();
        //保证同一个ip不能多次发送
        synchronized (ip){
            if(redisClient.get(RedisKey.USER_CHANGE_PASSWORD_CODE +email)!=null){
                return ResponseResult.booleanResp(ServiceStateCode.SMS_HAS_BEEN_SENT,false);
            }
            return sendEmail("sendChangePasswordToEmail","修改密码","mail_change_password_auth_code.html",email);
        }
    }

    /**
     * 发送修改密码的短信到指定的手机号(该手机号对应要修改密码的用户)里
     * @param phoneNumber 手机号(该手机号对应要修改密码的用户)
     * @return 返回发送状态，发送成功为true
     */
    @GetMapping(value = "/change/password/phone/send/{phoneNumber}",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> sendChangePasswordToPhone(HttpSession session,HttpServletRequest req,@PathVariable("phoneNumber") String phoneNumber){
        String ip = req.getRemoteHost();
        //保证同一个ip不能多次发送
        synchronized (ip){
            if(redisClient.get(RedisKey.USER_CHANGE_PASSWORD_CODE +phoneNumber)!=null){
                return ResponseResult.booleanResp(ServiceStateCode.SMS_HAS_BEEN_SENT,false);
            }

            return sendSMS("sendChangePasswordToPhone",phoneNumber,"SMS_154950909");
        }
    }

    /**
     * 发送注销邮件到指定的邮箱(该邮箱对应要注销的用户)里
     * @param email 邮箱(该邮箱对应要注销的用户)
     * @return 返回发送状态，发送成功为true
     */
    @GetMapping(value = "/cancellation/authCode/email/send/{email}",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> sendCancellationToEmail(HttpServletRequest req,@PathVariable("email") String email){
        String ip = req.getRemoteHost();
        //保证同一个ip不能多次发送
        synchronized (ip){
            if(redisClient.get(RedisKey.CANCELLATION_CODE +email)!=null){
                return ResponseResult.booleanResp(ServiceStateCode.SMS_HAS_BEEN_SENT,false);
            }
            return sendEmail("sendCancellationToEmail","注销账户","mail_cancellation_auth_code.html",email);
        }
    }

    /**
     * 发送注销短信到指定的手机号(该手机号对应要注销的用户)里
     * @param phoneNumber 手机号(该手机号对应要注销的用户)
     * @return 返回发送状态，发送成功为true
     */
    @GetMapping(value = "/cancellation/authCode/phone/send/{phoneNumber}",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> sendCancellationToPhone(HttpServletRequest req,@PathVariable("phoneNumber") String phoneNumber){
        String ip = req.getRemoteHost();
        //保证同一个ip不能多次发送
        synchronized (ip){
            if(redisClient.get(RedisKey.CANCELLATION_CODE +phoneNumber)!=null){
                return ResponseResult.booleanResp(ServiceStateCode.SMS_HAS_BEEN_SENT,false);
            }
            return sendSMS("sendCancellationToPhone",phoneNumber,"SMS_154950909");
        }
    }

    /**
     * 发送一封邮件
     * @param sendType 发送服务类型。比如：解绑、绑定、修改密码、登录等
     * @param subject 邮件的标题
     * @param templateHtml 邮件的模板
     * @param email 发送到的邮箱
     * @return 返回发送状态
     */
    public ResponseResult<Boolean> sendEmail(String sendType,String subject, String templateHtml, String email){
        //发送一个5位数字的授权码
        StringBuffer authCode = new StringBuffer();
        for(int i=1;i<=6;i++){
            authCode.append((int) (Math.random()*10));
        }
        MailTemplateGenerator template = new CodeTemplate(email,authCode.toString(),templateHtml);
        TemplateMail templateMail = new TemplateMail(email,subject,template);
        try {
            Method method = UserService.class.getMethod(sendType, TemplateMail.class, String.class);
            //发送邮件
            ServiceReturn<Boolean> result = (ServiceReturn<Boolean>) method.invoke(userService, templateMail, authCode.toString());
            //并返回发送状态
            return ResponseResult.auto(result.getStateCode(),result.getReturnData());
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.failure(ServiceStateCode.FAILURE);
        }
    }

    /**
     * 发送一封短信
     * @param sendType 发送服务类型。
     * @param phoneNumber 发送到的手机号
     * @param templateCode 短信模板
     * @return 返回发送状态
     */
    public ResponseResult<Boolean> sendSMS(String sendType,String phoneNumber,String templateCode){
        //发送一个5位数字的授权码
        StringBuffer authCode = new StringBuffer();
        for(int i=1;i<=6;i++){
            authCode.append((int) (Math.random()*10));
        }
        ShortMessage shortMessage = new ShortMessage()
                .setAcceptPhoneNumber(phoneNumber)
                .setSignName("阿里云短信测试")
                .setTemplateCode(templateCode)
                .setTemplateParam(new CodeTemplateParam(authCode.toString()))
                .setOutId("123456789");
        try {
            Method method = UserService.class.getMethod(sendType, ShortMessage.class, String.class);
            //发送短信
            ServiceReturn<Boolean> result = (ServiceReturn<Boolean>) method.invoke(userService, shortMessage, authCode.toString());
            //并返回发送状态
            return ResponseResult.auto(result.getStateCode(),result.getReturnData());
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.failure(ServiceStateCode.FAILURE);
        }
    }

    /**
     * 该用户绑定邮箱，注意：该功能只能登录后才能使用。因为修改的登录的用户的邮箱
     * @param email 要绑定的邮件
     * @param authCode 验证码
     * @return 返回绑定结果，如果绑定成功，则返回邮箱，否则返回null
     */
    @PostMapping("/bind/email")
    public ResponseResult<String> bindEmail(HttpSession session,String email,String authCode){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        ServiceReturn<String> result = userService.bindEmail(user, email, authCode);
        //不用对session里的变量进行更新，因为地址引用
        //session每次都是从redis中缓存拿的，所以我们所有的地址引用的方式都改不了值
        //如果想要该值，则必须写入到redis中session缓存里，
        //如果我们配置了set才触发写入redis的session里，则下面是必须的
//        session.setAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey(), user);
        return ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }


    /**
     * 该用户绑定手机号，注意：该功能只能登录后才能使用。因为修改的登录的用户的手机号
     * @param phoneNumber 要绑定的手机号
     * @param authCode 验证码
     * @return 返回绑定结果，如果绑定成功，则返回手机号，否则返回null
     */
    @PostMapping("/bind/phone")
    public ResponseResult<String> bindPhone(HttpSession session,String phoneNumber,String authCode){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());

        ServiceReturn<String> result = userService.bindPhone(user, phoneNumber, authCode);
        //不用对session里的变量进行更新，因为地址引用
        //session每次都是从redis中缓存拿的，所以我们所有的地址引用的方式都改不了值
        //如果想要该值，则必须写入到redis中session缓存里，
        //如果我们配置了set才触发写入redis的session里，则下面是必须的
//        session.setAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey(), user);
        return ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }

    /**
     * 进行邮箱的更改绑定
     * @param updateBindEmail 要更改的邮箱
     * @param authCode 验证码
     * @return 返回更改绑定后的邮箱
     */
    @PostMapping("/update/bind/email")
    public ResponseResult<String> updateBindEmail(HttpSession session,String updateBindEmail,String authCode){
        return bindEmail(session, updateBindEmail, authCode);
    }
    /**
     * 进行手机号更改绑定
     * @param updateBindPhone 要更改绑定的手机号
     * @param authCode 验证码
     * @return 返回更改绑定后的手机号
     */
    @PostMapping("/update/bind/phone")
    public ResponseResult<String> updateBindPhone(HttpSession session, String updateBindPhone,String authCode){
        return bindPhone(session, updateBindPhone, authCode);
    }

    /**
     * 进行绑定邮箱操作
     * @param authCode 授权码
     * @return 返回绑定状态，绑定成功返回true
     */
    @PostMapping("/unbind/email")
    public ResponseResult<Boolean> unbindEmail(HttpSession session,String authCode){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        ServiceReturn<Boolean> result = userService.unbindEmail(user, authCode);
        //不用对session里的变量进行更新，因为地址引用
        //session每次都是从redis中缓存拿的，所以我们所有的地址引用的方式都改不了值
        //如果想要该值，则必须写入到redis中session缓存里，
        //如果我们配置了set才触发写入redis的session里，则下面是必须的
//        session.setAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey(), user);
        return ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }
    /**
     * 进行绑定手机号操作
     * @param authCode 授权码
     * @return 返回绑定状态，绑定成功返回true
     */
    @PostMapping("/unbind/phone")
    public ResponseResult<Boolean> unbindPhone(HttpSession session,String authCode){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        ServiceReturn<Boolean> result = userService.unbindPhone(user, authCode);
        //不用对session里的变量进行更新，因为地址引用
        //session每次都是从redis中缓存拿的，所以我们所有的地址引用的方式都改不了值
        //如果想要该值，则必须写入到redis中session缓存里，
        //如果我们配置了set才触发写入redis的session里，则下面是必须的
//        session.setAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey(), user);
        return ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }


    /**
     * 更新用户的个人信息
     * @param infoParam 要更新的用户信息，如果不为null则该属性更新
     */
    @PostMapping(value = "/update/personalInfo",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseResult<UserPublicInfo> updateUserPersonalInfo(HttpSession session, UserPersonalInfoParam infoParam){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        //里面会自动更新session中的user对象
        ServiceReturn<User> result = userService.updateUserInfo(user,infoParam);
        UserPublicInfo userPublicInfo = UserPublicInfo.getUserPublicInfo(result.getReturnData());
        return ResponseResult.auto(result.getStateCode(),userPublicInfo);
    }

    /**
     * 登录后，进行密码的修改。
     * @param OldPassword 旧密码
     * @param newPassword 新密码
     * @return 返回修改状态
     */
    @PostMapping(value = "/update/password")
    public ResponseResult<Boolean> changePassword(HttpSession session,String OldPassword,String newPassword){
        User nowLoginUser = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        ServiceReturn<Boolean> result = userService.loginAfterChangePassword(nowLoginUser, OldPassword, newPassword);
        return ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }

    /**
     * 未登录也可以使用。通过邮箱修改密码，对该邮箱中修改密码邮件，进行验证，正确则修改该邮箱对应用户的密码
     * @param email 邮箱
     * @param newPassword 新密码
     * @param authCode 授权码
     * @return 返回修改状态
     */
    @PostMapping("/change/password/email")
    public ResponseResult<Boolean> changePasswordByEmail(HttpSession session, String email, String newPassword, String authCode){
        return changePasswordByEmailOrPhone(session,"userEmail",email,newPassword,authCode);
    }

    /**
     * 未登录也可以使用。通过手机号修改密码，对该手机号中修改密码手机号，进行验证，正确则修改该手机号对应用户的密码
     * @param phoneNumber 手机号
     * @param newPassword 新密码
     * @param authCode 授权码
     * @return 返回修改状态
     */
    @PostMapping("/change/password/phone")
    public ResponseResult<Boolean> changePasswordByPhone(HttpSession session, String phoneNumber, String newPassword, String authCode){
        return changePasswordByEmailOrPhone(session,"userPhone",phoneNumber,newPassword,authCode);
    }

    /**
     * 对该校验字段进行验证码的校验
     * @param checkField 校验字段，邮箱或者手机号
     * @param authCode 授权码
     * @return 返回校验状态
     */
    @PostMapping(value = "/change/password/check",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> checkChangePasswordAuthCode(String checkField,String authCode){
        ServiceReturn<Boolean> result = userService.checkChangePasswordAuthCode(checkField, authCode);
        return ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }

    /**
     * 获取当前登录用户的公共信息
     */
    @GetMapping(value = "/get/public/info",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<UserPublicInfo> getUserPublicInfo(HttpSession session){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        UserPublicInfo userPublicInfo = UserPublicInfo.getUserPublicInfo(user);
        return ResponseResult.success(userPublicInfo);
    }

    /**
     * 进行用户登出操作
     * 如果当前session中有用户正在线，则强制其下线
     */
    @GetMapping(value = "/loginOut",consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseResult<Boolean> loginOut(HttpSession session){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        if(user!=null){
            session.removeAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
            userService.loginOut(user);
        }
        return ResponseResult.success(true);
    }

    /**
     * 验证码方式进行注销账户
     * @param authCode 授权码
     * @return 返回注销状态，注销成功返回true
     */
    @PostMapping("/cancellation/authCode")
    public ResponseResult<Boolean> cancellationByAuthCode(HttpSession session,String authCode){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        ServiceReturn<Boolean> result = userService.cancellationByAuthCode(user, authCode);
        session.removeAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        return ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }

    /**
     * 密码方式进行注销账户
     * @param password 密码
     * @return 返回注销状态，注销成功返回true
     */
    @PostMapping("/cancellation/password")
    public ResponseResult<Boolean> cancellationByPassword(HttpSession session,String password){
        User user = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        ServiceReturn<Boolean> result = userService.cancellationByPassword(user, password);
        session.removeAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
        return ResponseResult.auto(result.getStateCode(),result.getReturnData());
    }

    /**
     * 邮箱或者手机号方式进行修改密码
     * @param field user类字段名，比如：如果是邮箱方式，则是UserEmail
     * @param fieldValue 对应的字段值。比如：2412594046@qq.com
     * @param newPassword 修改后的密码
     * @param authCode 授权码
     * @return 返回修改状态，修改成功返回true
     */
    public ResponseResult<Boolean> changePasswordByEmailOrPhone(HttpSession session,String field, String fieldValue, String newPassword, String authCode){
        //校验验证码的正确性
        ServiceReturn<Boolean> checkResult = userService.checkChangePasswordAuthCode(fieldValue, authCode);
        //如果校验结果正确，则更新密码
        if(checkResult.getReturnData()){
            User nowLoginUser = (User) session.getAttribute(SessionAttributeKey.NOW_LOGIN_USER.getKey());
            ServiceReturn<Boolean> result;
            //如果是当前登录的用户，则进行当前用户的密码更新
            try {
                String declaredFieldValue = null;
                if(nowLoginUser!=null){
                    Field declaredField = User.class.getDeclaredField(field);
                    declaredField.setAccessible(true);
                    declaredFieldValue = (String) declaredField.get(nowLoginUser);
                }
                if(fieldValue.equals(declaredFieldValue)){
                    result  = userService.loginAfterChangePassword(nowLoginUser, nowLoginUser.getUserPassword(), newPassword);
                }else{
                    //进行通过修改邮箱的方式修改密码
                    result  = userService.notLoginChangePassword(fieldValue,newPassword);
                }
                return ResponseResult.auto(result.getStateCode(),result.getReturnData());
            } catch (Exception e) {
                e.printStackTrace();
                return ResponseResult.failure(ServiceStateCode.FAILURE);
            }
        }
        return ResponseResult.auto(checkResult.getStateCode(),checkResult.getReturnData());
    }
}
